
package Gui;

import static controller.Globals.VERTEX_SIZE_HIGHLIGHTED;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.swing.JPanel;
import javax.swing.Scrollable;

import datatypes.Edge;
import datatypes.Vertex;

public class TriangleDisplay extends JPanel implements MouseListener, MouseMotionListener, KeyListener, Scrollable
{
	private static final long serialVersionUID = -8006175897441108518L;
	
	
	MainWindow mainWindow; 
	private List<Edge> edges = null;
	private Vector<Vertex> vertices = new Vector<Vertex>();
	private Set<Vertex> selected = new HashSet<Vertex>();
	private double zoom = 1.0;
	
	private Vertex clickPoint;
	
	private AffineTransform transform = new AffineTransform();

	public TriangleDisplay(MainWindow main)
	{
		super();
		addMouseListener(this); 
        addMouseMotionListener(this); 
        addKeyListener(this); 
        mainWindow = main;
        this.setPreferredSize(new Dimension(1200,800));
        this.setBackground(Color.white);
	}
	
	public void setZoom(double z) {
		zoom = z;
		if (z > 1.0)
			setPreferredSize( new Dimension( (int)(z * 1200), (int)( z * 800 ) ));
		repaint();
		doLayout();
		mainWindow.doLayout();
		revalidate();
	}
	
	public void spawnRandomPoints(int numPoints) 
    { 
        synchronized(vertices) 
        { 
            vertices.clear();
            selected.clear();
            vertices.addAll(tools.RandomVerticesGenerator.randomVertices(getViewportWidth(), getViewportHeight(), 5.0, numPoints)); 
            System.out.println(vertices.size());
            processChanges(); 
        } 
    } 
     
    public void spawnLoadPoints(String state) 
    { 
        synchronized(vertices) 
        { 
            vertices.clear();
            selected.clear();
            try { 
                vertices.addAll(tools.OpenSaveVertices.loadVertices(state)); 
            } catch (IOException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } catch (ClassNotFoundException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
            System.out.println(vertices.size());
            processChanges(); 
        } 
    } 
     
    public void spawnSavePoints(String state) 
    { 
        synchronized(vertices) 
        { 
            Vector<Vertex> vertsToSave = new Vector<Vertex>(); 
            for (int i = 0; i < vertices.size(); i++) vertsToSave.addElement(vertices.get(i)); 
            try { 
                tools.OpenSaveVertices.saveVertices(vertsToSave, state); 
            } catch (IOException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
        } 
    } 
     
    Vector<Vertex> getVertices() 
    { 
        return vertices; 
    }
    
//    public void setVertices(Vector<Vertex> vertices) {
//		this.vertices = vertices;
//	}
     
    public int getViewportWidth() 
    {
    	return this.getWidth();
//        return 1000; 
    } 
    public int getViewportHeight() 
    {
    	return this.getHeight();
//        float aspectRatio = ((float)getHeight())/getWidth(); 
//        return (int)(getViewportWidth()*aspectRatio); 
    }
	
	
	
	public void showMesh(List<Edge> edges)
	{
		this.edges = edges;
		updateUI();
	}	
	
	public void reset()
	{
		this.edges = null;
		this.vertices.clear();
		this.selected.clear();
		updateUI();		
	}
	
	public void processChanges() 
    { 
        updateUI(); 
        mainWindow.triangulate(vertices); 
    } 

	public void paintComponent(Graphics graphics)
	{
		super.paintComponent(graphics);
		
		Graphics2D g = (Graphics2D) graphics.create();
		AffineTransform savedTransform = g.getTransform();
		AffineTransform toCenterTransform = new AffineTransform();
		transform.setToScale(zoom, zoom);
		toCenterTransform.concatenate(transform);
//		toCenterTransform.rotate(1);
		g.transform(toCenterTransform);
		int width = getWidth();
		int height = getHeight();
		g.setColor(Color.white);
		g.fillRect(0, 0, width, height);
		if (edges == null || vertices == null) return;
		
		
		for (Edge edge : edges)
		{
			edge.draw(g, this);
		}
		
		g.setColor(Color.black);
		
		for (Vertex v : vertices) {
			v.draw(g, this);
		}
		g.setTransform(savedTransform);
	}
	
	
    @Override 
    public void mouseClicked(MouseEvent arg0) 
    {
    	
    } 
 
    @Override 
    public void mouseEntered(MouseEvent arg0) 
    { 
        requestFocusInWindow(); 
    } 
 
    @Override 
    public void mouseExited(MouseEvent arg0) 
    { 
        requestFocusInWindow(false); 
    }
    
    private Vertex location(MouseEvent event) {
    	clickPoint = new Vertex((double) event.getX(), (double) event.getY());
		try {
			clickPoint.applyTransform(transform.createInverse());
		} catch (NoninvertibleTransformException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return clickPoint;
    }
 
	@Override
	public void mousePressed(MouseEvent event) {
		clickPoint = location(event);
		if ( ! event.isShiftDown()) {
			for (Vertex previouslySelected : selected) {
				previouslySelected.selected = false;
			}
			selected.clear();
		}
		boolean addVertex = true;
		for (Vertex tested : vertices) {
			boolean hit = tested.isInCircle(clickPoint, VERTEX_SIZE_HIGHLIGHTED);
			if (hit) {
				addVertex = false;
				tested.selected = true;
				selected.add(tested);
				break;
			}
		}
		if (addVertex) {
			vertices.add(clickPoint.clone());
		}
      processChanges(); 
	}
 
    @Override 
    public void mouseReleased(MouseEvent arg0) 
    {
    	processChanges();
    } 
 
    @Override 
    public void mouseDragged(MouseEvent event) 
    {
        Vertex delta = new Vertex((double)event.getX()/zoom, (double)event.getY()/zoom);
        delta.minus(clickPoint);
        clickPoint.plus(delta);
        for (Vertex sel : selected) 
        {
        	sel.plus(delta);
        } 
        processChanges(); 
    } 
 
    @Override 
    public void mouseMoved(MouseEvent arg0) 
    { 
         
    } 
 
	@Override
	public void keyPressed(KeyEvent arg0) {
		if (arg0.getKeyCode() == KeyEvent.VK_DELETE) {
			for (Vertex sel : selected) {
				vertices.remove(sel);
			}
			selected.clear();
			processChanges();
		}
	}
 
    @Override 
    public void keyReleased(KeyEvent arg0) 
    {     
    } 
 
    @Override 
    public void keyTyped(KeyEvent arg0) 
    { 
    }

	@Override
	public Dimension getPreferredScrollableViewportSize() {
		return new Dimension(10, 10);
	}

	@Override
	public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) {
		// TODO Auto-generated method stub
		return 5;
	}

	@Override
	public boolean getScrollableTracksViewportHeight() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean getScrollableTracksViewportWidth() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) {
		// TODO Auto-generated method stub
		return 5;
	}
}
